package com.qixiaobao.nondirect.invoice.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.qixiaobao.common.config.SupplyConfig;
import com.qixiaobao.common.core.domain.model.LoginUser;
import com.qixiaobao.common.core.rabbitmq.config.RabbitMqConfig;
import com.qixiaobao.common.core.rabbitmq.server.MsgSendServer;
import com.qixiaobao.common.exception.UtilException;
import com.qixiaobao.common.utils.*;
import com.qixiaobao.common.utils.easyExcel.EasyExcelUtil;
import com.qixiaobao.common.utils.file.FileUtils;
import com.qixiaobao.common.utils.ocr.OcrFile;
import com.qixiaobao.common.utils.uuid.IdUtils;
import com.qixiaobao.insure.domain.MatchInsure;
import com.qixiaobao.insure.mapper.MatchInsureMapper;
import com.qixiaobao.nondirect.invoice.domain.Bill;
import com.qixiaobao.nondirect.invoice.domain.BillInsureFee;
import com.qixiaobao.nondirect.invoice.domain.BillMerge;
import com.qixiaobao.nondirect.invoice.domain.BillMergeBill;
import com.qixiaobao.nondirect.invoice.domain.domain.InvoiceFile;
import com.qixiaobao.nondirect.invoice.domain.dto.NonDirectInvoiceDTO;
import com.qixiaobao.nondirect.invoice.domain.vo.NonDirectBillVO;
import com.qixiaobao.nondirect.invoice.domain.vo.NonDirectWaitInvoiceChargeVO;
import com.qixiaobao.nondirect.invoice.mapper.BillInsureFeeMapper;
import com.qixiaobao.nondirect.invoice.mapper.BillMapper;
import com.qixiaobao.nondirect.invoice.mapper.BillMergeMapper;
import com.qixiaobao.nondirect.invoice.mapper.InvoiceFileMapper;
import com.qixiaobao.nondirect.settle.domain.InsureFee;
import com.qixiaobao.nondirect.settle.mapper.InsureFeeMapper;
import com.qixiaobao.nondirect.invoice.domain.domain.*;
import com.qixiaobao.system.domain.Dict;
import com.qixiaobao.system.domain.SignCompany;
import com.qixiaobao.system.domain.dto.ChargeDTO;
import com.qixiaobao.system.mapper.*;
import com.qixiaobao.nondirect.invoice.service.IBillService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @Description 开票账单 业务层处理
 * @Author yangxm
 * @Date 2025/7/4 16:15
 */
@Slf4j
@Service
public class BillServiceImpl implements IBillService {

    @Autowired
    private InsureFeeMapper insureFeeMapper;
    @Autowired
    private BillMapper billMapper;
    @Autowired
    private DictMapper dictMapper;
    @Autowired
    private BillMergeMapper billMergeMapper;
    @Autowired
    private MatchInsureMapper matchInsureMapper;
    @Autowired
    private SignCompanyMapper signCompanyMapper;
    @Autowired
    private InvoiceFileMapper invoiceFileMapper;
    @Autowired
    private MsgSendServer msgSendServer;

    /**
     * @Description 非直投发票管理-待开票-开票操作
     * @Author yangxm
     * @Date 2025/7/4 16:22
     * @param bill
     * @return int
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int makeInvoiceBill(Bill bill) {
        long begin = System.currentTimeMillis();
        log.info("非直投发票管理-待开票-开票操作，参数：{}", bill);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null){
            throw new UtilException("获取登录信息失败，请重新登录！");

        }
        //计算增员人数汇总，减员人数汇总，实付保费总计
        InsureFee insureFee = insureFeeMapper.insureFeeSummaryByIdList(bill.getInsureFeeIdList());
        //根据保费ids查询实际付款单位
        List<Long> signcompanyList = insureFeeMapper.getSignCompanyByInsureFeeIdList(bill.getInsureFeeIdList());
        if (StringUtils.isEmpty(signcompanyList) || signcompanyList.size() != 1){
            throw new UtilException("不能同时结算多个实际付款单位！");
        }
        bill.setPayCompanyId(signcompanyList.get(0));
        bill.setAddNum(insureFee.getAddNum());
        bill.setMinusNum(insureFee.getMinusNum());
        //实际开票费用总计
        bill.setInsureFeeTotal(insureFee.getActualInsureFee());
        bill.setBillStatus("0");
        bill.setRemarkOperator(loginUser.getUsername());
        bill.setRemarkTime(DateUtils.getNowDate());
        int row = billMapper.insertBill(bill);
        //保存发票与保费结算关联表
        List<BillInsureFee> addList = new ArrayList<>();
        BillInsureFee billInsureFee = null;
        for (int i = 0; i < bill.getInsureFeeIdList().size(); i++) {
            billInsureFee = new BillInsureFee();
            billInsureFee.setBillId(bill.getId());
            billInsureFee.setInsureFeeId(bill.getInsureFeeIdList().get(i));
            //封装list
            addList.add(billInsureFee);
        }
        //批量保存到数据库
        MybatisBatchUtils.batchUpdateOrInsert(addList, BillInsureFeeMapper.class, BillInsureFeeMapper::insertBillInsureFee);
        log.info("非直投发票管理-待开票-开票操作完成，总耗时：{}ms", System.currentTimeMillis() - begin);
        return row;
    }

    /**
     * @Description 非直投发票管理-开票中列表
     * @Author yangxm
     * @Date 2025/7/4 16:46
     * @param nonDirectInvoiceDTO
     * @return java.util.List<com.qixiaobao.nondirect.invoice.domain.Bill>
     */
    @Override
    public List<NonDirectBillVO> selectInvoicingList(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        long begin = System.currentTimeMillis();
        log.info("查询非直投发票管理-开票中列表，参数：{}", nonDirectInvoiceDTO);
        if (StringUtils.isBlank(nonDirectInvoiceDTO.getBillStatus()) || !"0".equals(nonDirectInvoiceDTO.getBillStatus())){
            throw new UtilException("参数：开票状态错误！");
        }
        List<NonDirectBillVO> nonDirectBillVOList = billMapper.selectInvoicingList(nonDirectInvoiceDTO);
        log.info("查询非直投发票管理-开票中列表sql，耗时：{}ms", System.currentTimeMillis() - begin);
        return nonDirectBillVOList;
    }

    /**
     * @Description 非直投发票管理-开票中列表-开票金额
     * @Author yangxm
     * @Date 2025/7/4 16:46
     * @param nonDirectInvoiceDTO
     * @return java.math.BigDecimal
     */
    @Override
    public BigDecimal selectInvoicingTotal(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        return billMapper.selectInvoicingTotal(nonDirectInvoiceDTO);
    }

    /**
     * @Description 非直投发票管理-开票中列表-导出
     * @Author yangxm
     * @Date 2025/7/7 9:16
     * @param nonDirectInvoiceDTO
     * @param response
     */
    @Override
    public void invoicingExport(NonDirectInvoiceDTO nonDirectInvoiceDTO, HttpServletResponse response) {
        long begin = System.currentTimeMillis();
        log.info("查询非直投发票管理-开票中列表导出，参数：{}", nonDirectInvoiceDTO);
        //导出表头
        List<String> arrayHeaders = new ArrayList<>();
        arrayHeaders.add("序号");
        arrayHeaders.add("税号");
        arrayHeaders.add("公司注册地址");
        arrayHeaders.add("公司注册电话");
        arrayHeaders.add("开户银行");
        arrayHeaders.add("银行账号");
        arrayHeaders.add("供应商全称");
        arrayHeaders.add("供应商简称");
        arrayHeaders.add("付款单位");
        arrayHeaders.add("开票金额");
        arrayHeaders.add("发票类型");
        arrayHeaders.add("操作时间");
        arrayHeaders.add("备注");
        //动态表头
        List<List<String>> headList = new ArrayList<>();
        String headStr;
        List<String> headArray;
        for (int i = 0; i < arrayHeaders.size(); i++) {
            headStr = arrayHeaders.get(i);
            headArray = new ArrayList<>();
            headArray.add(headStr);
            headList.add(headArray);
        }
        //动态表头的数据
        List<List<Object>> datalist = new ArrayList<>();
        List<Object> obj = null;
        List<NonDirectBillVO> nonDirectBillVOList = selectInvoicingList(nonDirectInvoiceDTO);
        if (StringUtils.isNotEmpty(nonDirectBillVOList)){
            NonDirectBillVO item;
            for (int i = 0; i < nonDirectBillVOList.size(); i++) {
                item = nonDirectBillVOList.get(i);
                obj = new ArrayList<>();
                obj.add(i + 1);
                obj.add(item.getTaxNum());
                obj.add(item.getCompanyAddress());
                obj.add(item.getCompanyPhone());
                obj.add(item.getBankDeposit());
                obj.add(item.getBankNum());
                obj.add(item.getSupplyName());
                obj.add(item.getSupplyShortName());
                obj.add(item.getActualPayCompanyName());
                obj.add(item.getInsureFeeTotal());
                obj.add(item.getInvoiceTypeVal());
                obj.add(Optional.ofNullable(item.getUpdateTime()).map(x-> DateFormatUtils.format(x, "yyyy/MM/dd HH:mm:ss")).orElse(""));
                obj.add(item.getRemark());
                datalist.add(obj);
            }
        }
        try {
            EasyExcelUtil.writeExcel(response, datalist, headList, "非直投发票管理-开票中", "非直投发票管理-开票中");
        } catch (IOException e) {
            log.error(" 非直投发票管理-开票中-导出异常：", e);
            throw new RuntimeException("导出失败：" + e.getMessage());
        }
    }

    /**
     * @Description 更新备注
     * @Author yangxm
     * @Date 2025/7/7 9:34
     * @param nonDirectInvoiceDTO
     * @return int
     */
    @Override
    public int updateRemark(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        long begin = System.currentTimeMillis();
        log.info(" 非直投发票管理-更新备注：{}", nonDirectInvoiceDTO);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null){
            throw new UtilException("登录已失效，请重新登录！");
        }
        int row;
        if (nonDirectInvoiceDTO.getBillId() != null && nonDirectInvoiceDTO.getBillMergeId() == null){
            Bill oldBill = billMapper.selectBillById(nonDirectInvoiceDTO.getBillId());
            if(oldBill == null) {
                log.error("非直投发票管理备注修改失败，未找到发票数据，发票ID：{}", nonDirectInvoiceDTO.getBillId());
                throw new IllegalArgumentException("未找到对应数据！");
            }
            Bill bill = new Bill();
            bill.setId(nonDirectInvoiceDTO.getBillId());
            bill.setRemark(nonDirectInvoiceDTO.getRemark());
            bill.setRemarkOperator(loginUser.getUsername());
            bill.setRemarkTime(DateUtils.getNowDate());
            row = billMapper.updateBill(bill);
        } else if (nonDirectInvoiceDTO.getBillId() == null && nonDirectInvoiceDTO.getBillMergeId() != null) {
            BillMerge oldBillMerge = billMergeMapper.selectBillMergeById(nonDirectInvoiceDTO.getBillMergeId());
            if (oldBillMerge == null){
                log.error("非直投发票管理备注修改失败，未找到合并发票数据，合并发票ID：{}", nonDirectInvoiceDTO.getBillMergeId());
                throw new IllegalArgumentException("未找到对应数据！");
            }
            BillMerge billMerge = new BillMerge();
            billMerge.setId(nonDirectInvoiceDTO.getBillMergeId());
            billMerge.setRemark(nonDirectInvoiceDTO.getRemark());
            billMerge.setRemarkOperator(loginUser.getUsername());
            billMerge.setRemarkTime(DateUtils.getNowDate());
            row = billMergeMapper.updateBillMerge(billMerge);
        }else {
            log.error("非直投发票管理备注修改失败，数据异常：发票ID：{}, 合并发票ID：{}", nonDirectInvoiceDTO.getBillId(), nonDirectInvoiceDTO.getBillMergeId());
            throw new UtilException("数据异常，请联系客服！");
        }
        log.info("非直投发票管理备注修改完成，总耗时：{}", System.currentTimeMillis() - begin);
        return row;
    }

    /**
     * @Description 开票中 - 上传发票
     * @Author yangxm
     * @Date 2025/7/7 9:57
     * @param nonDirectInvoiceDTO
     * @return int
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int uploadInvoicingFile(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        if (nonDirectInvoiceDTO.getBillId() == null){
            throw new UtilException("参数错误！");
        }
        if (StringUtils.isEmpty(nonDirectInvoiceDTO.getOcrFileList())){
            throw new UtilException("请上传发票！");
        }

        Bill oldBill = billMapper.selectBillById(nonDirectInvoiceDTO.getBillId());
        if (oldBill == null){
            throw new UtilException("数据不存在，请刷新页面后重试！");
        }
        Bill bill = new Bill();
        bill.setId(nonDirectInvoiceDTO.getBillId());
        bill.setBillStatus("2");
        bill.setAuditStatus("0");
        bill.setInvoiceTotalAmount(nonDirectInvoiceDTO.getInvoiceTotalAmount());
        bill.setTaxRate(nonDirectInvoiceDTO.getTaxRate());
        bill.setInvoiceTypeVal(nonDirectInvoiceDTO.getInvoiceTypeVal());
        int row = billMapper.updateBill(bill);
        //保存发票文件
        saveInvoiceFile(nonDirectInvoiceDTO, bill, null);
        return row;
    }

    /**
     * @Description 保存发票文件
     * @Author yangxm
     * @Date 2025/7/7 13:35
     * @param nonDirectInvoiceDTO
     */
    private void saveInvoiceFile(NonDirectInvoiceDTO nonDirectInvoiceDTO, Bill bill, BillMerge billMerge) {
        List<OcrFile> ocrFileList = nonDirectInvoiceDTO.getOcrFileList();
        if (StringUtils.isEmpty(ocrFileList)){
            throw new UtilException("请上传发票文件！");
        }
        List<InvoiceFile> invoiceFileList = new ArrayList<>();
        InvoiceFile invoiceFile;
        OcrFile ocrFile;
        for (int i = 0; i < ocrFileList.size(); i++) {
            ocrFile = ocrFileList.get(i);
            invoiceFile = new InvoiceFile();
            invoiceFile.setBillId(nonDirectInvoiceDTO.getBillId());
            //页面选择的税率
            invoiceFile.setTaxRate(nonDirectInvoiceDTO.getTaxRate());
            invoiceFile.setInvoiceUrl(ocrFile.getUrl());
            invoiceFile.setFileId(ocrFile.getFileId());
            invoiceFile.setInvoiceNum(ocrFile.getInvoiceNum());
            invoiceFile.setInvoiceDate(ocrFile.getInvoiceDate());
            invoiceFile.setInvoiceMoney(ocrFile.getInvoiceMoney());
            //封装到list
            invoiceFileList.add(invoiceFile);
        }
        //批量保存发票文件
        MybatisBatchUtils.batchUpdateOrInsert(invoiceFileList, InvoiceFileMapper.class, InvoiceFileMapper::insertInvoiceFile);
        //开票月份和最大发票金额对应的发票时间处理
        if (StringUtils.isNotEmpty(invoiceFileList)){
            //开票月份
            String invoiceYearMonth;
            //最大发票金额对应的发票时间
            Date maxInvoiceMoneyInvoiceDate = null;
            //将invoiceFiles中invoiceDate的时间格式转换为年月格式
            List<String> invoiceDateStrList = invoiceFileList.stream().map(InvoiceFile::getInvoiceDate).filter(Objects::nonNull).map(DateUtils::yearMonth).collect(Collectors.toList());
            //多个invoiceDate的时间拼接成字符串
            invoiceYearMonth = StringUtils.join(invoiceDateStrList, ",");
            //取出invoiceFiles中invoiceMoney最大值对应的时间，若金额相同则取任意一个
            Optional<InvoiceFile> maxInvoiceFile = invoiceFileList.stream().max(Comparator.comparing(InvoiceFile::getInvoiceMoney));
            if (maxInvoiceFile.isPresent()){
                log.info("发票文件信息：{}", maxInvoiceFile);
                maxInvoiceMoneyInvoiceDate = maxInvoiceFile.get().getInvoiceDate();
            }
            if (billMerge != null){
                billMerge.setInvoiceYearMonth(invoiceYearMonth);
                billMerge.setMaxInvoiceMoneyInvoiceDate(maxInvoiceMoneyInvoiceDate);
                //更新到数据库
                billMergeMapper.updateMaxInvoiceDate(billMerge);
            }else {
                bill.setInvoiceYearMonth(invoiceYearMonth);
                bill.setMaxInvoiceMoneyInvoiceDate(maxInvoiceMoneyInvoiceDate);
                //更新到数据库
                billMapper.updateMaxInvoiceDate(bill);
            }
        }
    }

    /**
     * @Description 开票中 - 合并开票
     * @Author yangxm
     * @Date 2025/7/7 11:13
     * @param nonDirectInvoiceDTO
     * @return int
     */
    @Override
    public int uploadMergeInvoiceFile(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        if (StringUtils.isEmpty(nonDirectInvoiceDTO.getBillIdList())){
            throw new UtilException("参数错误！");
        }
        if (StringUtils.isEmpty(nonDirectInvoiceDTO.getOcrFileList())){
            throw new UtilException("请上传发票！");
        }
        int row = 0;
        List<Bill> billList = billMapper.selectBillListByIdList(nonDirectInvoiceDTO.getBillIdList());
        if (StringUtils.isEmpty(billList)){
            throw new UtilException("未查到开票信息！");
        }
        //判断发票合并，是否包含多个供应商全称
        List<Bill> supplyBillList = billList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()-> new TreeSet<>(Comparator.comparing(Bill::getSupplyName))), ArrayList::new));
        if (StringUtils.isNotEmpty(supplyBillList) && supplyBillList.size() != 1){
            throw new UtilException("发票合并的数据不能包含多个供应商全称，请检查！");
        }
        //判断发票合并，是否包含多个付款单位
        List<Bill> signCompanyBillList = billList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()-> new TreeSet<>(Comparator.comparing(Bill::getPayCompanyId))), ArrayList::new));
        if (StringUtils.isNotEmpty(signCompanyBillList) && signCompanyBillList.size() != 1){
            throw new UtilException("发票合并的数据不能包含多个付款单位，请检查！");
        }
        Bill oldBill;
        Bill bill;
        for (int i = 0; i < billList.size(); i++) {
            oldBill = billList.get(i);
            if (!"0".equals(oldBill.getBillStatus())){
                throw new UtilException("存在已经开票过的数据，不能重复开票，请刷新页面重新合并发票！");
            }
            bill = new Bill();
            bill.setId(oldBill.getId());
            //开票状态：待审核
            bill.setBillStatus("2");
            //财务审核状态：待审
            bill.setAuditStatus("0");
            //合并标识
            bill.setMergeFlag("1");
            //更新开票状态和财务审核状态
            billMapper.updateBill(bill);
        }
        //开票金额
        BigDecimal insureFeeTotal = billList.stream().map(Bill::getInsureFeeTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
        //非直投发票合并开票
        BillMerge billMerge = new BillMerge();
        //开票状态：待审核
        billMerge.setBillStatus("2");
        //财务审核状态：待审
        billMerge.setAuditStatus("0");
        billMerge.setSupplyName(supplyBillList.get(0).getSupplyName());
        billMerge.setSignCompany(signCompanyBillList.get(0).getPayCompanyId());
        billMerge.setInsureFeeTotal(insureFeeTotal);
        billMerge.setInvoiceTotalAmount(nonDirectInvoiceDTO.getInvoiceTotalAmount());
        billMerge.setInvoiceTypeVal(nonDirectInvoiceDTO.getInvoiceTypeVal());
        billMerge.setTaxRate(nonDirectInvoiceDTO.getTaxRate());
        //保存入库
        row = billMergeMapper.insertBillMerge(billMerge);
        //非直投开票信息与合并开票信息关联表
        BillMergeBill billMergeBill;
        for (int i = 0; i < nonDirectInvoiceDTO.getBillIdList().size(); i++) {
            billMergeBill = new BillMergeBill();
            billMergeBill.setBillMergeId(billMerge.getId());
            billMergeBill.setBillId(nonDirectInvoiceDTO.getBillIdList().get(i));
            billMergeMapper.insertBillMergeBill(billMergeBill);
        }
        //发票文件处理
        saveInvoiceFile(nonDirectInvoiceDTO, null, billMerge);
        return row;
    }

    /**
     * @Description 开票中、待审核、已开票 - 费用详情
     * @Author yangxm
     * @Date 2025/7/7 13:57
     * @param chargeDTO
     * @return java.util.List<com.qixiaobao.nondirect.invoice.domain.vo.NonDirectWaitInvoiceChargeVO>
     */
    @Override
    public List<NonDirectWaitInvoiceChargeVO> selectChargeList(ChargeDTO chargeDTO) {
        long begin = System.currentTimeMillis();
        log.info("查询非直投发票管理 - 开票中、待审核、已开票 - 费用详情, 参数：{}", chargeDTO);
        List<NonDirectWaitInvoiceChargeVO> list = billMapper.selectInvoiceChargeList(chargeDTO);
        log.info("查询非直投发票管理 - 开票中、待审核、已开票 - 费用详情sql, 耗时：{}ms", System.currentTimeMillis() - begin);
        if (StringUtils.isNotEmpty(list)){
            //获取所有的保单id
            List<Long> insureIdList = list.stream().map(NonDirectWaitInvoiceChargeVO::getInsureId).distinct().collect(Collectors.toList());
            //根据保单id，查询匹配后供应商简称
            List<MatchInsure> matchInsureList = matchInsureMapper.selectToSupplyShortNameList(insureIdList);
            Map<Long,String> toSupplyShortNameMap = null;
            if (StringUtils.isNotEmpty(matchInsureList)){
                toSupplyShortNameMap = matchInsureList.stream().collect(Collectors.toMap(MatchInsure::getFromInsureId, MatchInsure::getToSupplyShortName));
            }

            // 字典
            List<Dict> dictList = dictMapper.selectDictList();
            Map<String, Map<String, String>> dictMap = new ConcurrentHashMap<>();
            if (StringUtils.isNotEmpty(dictList)) {
                dictMap = dictList.stream()
                        .filter(c -> c.getVal() != null)
                        .collect(Collectors.groupingBy(Dict::getType, Collectors.toMap(Dict::getK, Dict::getVal)));
            }
            Map<String, String> insurePeriodMap = dictMap.get("insureperiod");
            Map<String, String> mealnameMap = dictMap.get("mealname");
            Map<String, String> maimRatioMap = dictMap.get("maimRatio");
            Map<String, String> jobtypeMap = dictMap.get("jobtype");

            //付款单位
            Map<Long, String> payCompanyNameMap = signCompanyMapper.selectSignCompanyNameList().stream()
                    .collect(Collectors.toMap(SignCompany::getId, SignCompany::getCompanyName));

            // 发票文件
            Map<Long, InvoiceFile> invoiceFileMap = null;
            //根据保费id查询发票信息
            List<InvoiceFile> invoiceFileList = invoiceFileMapper.selectInvoiceFileByInsureFeeId(chargeDTO.getInsureFeeId());
            if (StringUtils.isNotEmpty(invoiceFileList)){
                invoiceFileMap = invoiceFileList.stream().collect(Collectors.toMap(InvoiceFile::getInsureFeeId, e -> e));
            }
            NonDirectWaitInvoiceChargeVO item;
            InvoiceFile invoiceFile;

            for (int i = 0; i < list.size(); i++) {
                item = list.get(i);
                item.setIndex(i + 1);
                item.setMealNameVal(mealnameMap.get(item.getMealName()));
                item.setMaimRatioVal(maimRatioMap.get(item.getMaimRatio()));
                item.setJobTypeVal(jobtypeMap.get(item.getJobType()));
                item.setInsurePeriodVal(insurePeriodMap.get(item.getInsurePeriod()));
                item.setInsurePerson("增员:" + item.getAddNum() + ";减员:" + item.getMinusNum());
                item.setAbandonFlagVal(item.getAbandonFlag() == 1 ? "是" : "否");
                item.setFinanceReckonVal(StringUtils.equals("0", item.getFinanceReckon()) ? "否" : "是");
                item.setPayCompanyName(payCompanyNameMap.get(item.getPayCompanyId()));
                item.setActualPayCompanyName(payCompanyNameMap.get(item.getActualPayCompanyId()));
                if (StringUtils.isNotEmpty(invoiceFileMap)){
                    invoiceFile = invoiceFileMap.get(item.getInsureFeeId());
                    if (invoiceFile != null){
                        item.setInvoiceNums(invoiceFile.getInvoiceNums());
                        item.setInvoiceDates(invoiceFile.getInvoiceDates());
                        item.setInvoiceMoneys(invoiceFile.getInvoiceMoneys());
                    }
                }
                if (StringUtils.isNotEmpty(toSupplyShortNameMap)){
                    item.setToSupplyShortName(toSupplyShortNameMap.getOrDefault(item.getInsureId(), ""));
                }
            }
        }
        log.info("查询非直投发票管理 - 开票中、待审核、已开票 - 费用详情, 总耗时：{}ms", System.currentTimeMillis() - begin);
        return list;
    }

    /**
     * @Description 待审核、已开票 - 列表
     * @Author yangxm
     * @Date 2025/7/7 14:30
     * @param nonDirectInvoiceDTO
     * @return java.util.List<com.qixiaobao.nondirect.invoice.domain.vo.NonDirectBillVO>
     */
    @Override
    public List<NonDirectBillVO> selectInvoiceList(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        long begin = System.currentTimeMillis();
        if (StringUtils.isBlank(nonDirectInvoiceDTO.getBillStatus())){
            throw new UtilException("参数：开票状态不能为空！");
        }
        String billStatus = nonDirectInvoiceDTO.getBillStatus();
        String billStatusVal;
        if ("2".equals(billStatus)){
            billStatusVal = "待审核";
        }else if ("1".equals(billStatus)){
            billStatusVal = "已开票";
        }else {
            throw new UtilException("参数：开票状态有误！");
        }
        log.info("查询非直投发票管理-{}列表，参数：{}", billStatusVal, nonDirectInvoiceDTO);
        List<NonDirectBillVO> list = billMapper.selectInvoiceList(nonDirectInvoiceDTO);
        log.info("查询非直投发票管理-{}列表sql，耗时：{}ms", billStatusVal, System.currentTimeMillis() - begin);
        if (StringUtils.isNotEmpty(list)){
            NonDirectBillVO item;
            for (int i = 0; i < list.size(); i++) {
                item = list.get(i);
                //计算发票差额
                item.setInvoiceBalance(item.getInsureFeeTotal().subtract(item.getInvoiceTotalAmount() == null ? BigDecimal.ZERO : item.getInvoiceTotalAmount()));
            }
        }
        log.info("查询非直接发票管理-{}列表结束，总耗时：{}ms", billStatusVal, System.currentTimeMillis() - begin);
        return list;
    }

    /**
     * @Description 待审核、已开票 - 列表 - 应开票金额、实际开票金额总计
     * @Author yangxm
     * @Date 2025/7/7 15:39
     * @param nonDirectInvoiceDTO
     * @return java.util.Map<java.lang.String,java.math.BigDecimal>
     */
    @Override
    public Map<String, BigDecimal> selectInvoiceTotal(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        Map<String, BigDecimal> resultMap = new HashMap<>();
        BigDecimal insureFeeTotal = BigDecimal.ZERO;
        BigDecimal invoiceTotalAmount = BigDecimal.ZERO;
        List<NonDirectBillVO> list = billMapper.selectInvoiceList(nonDirectInvoiceDTO);
        if (StringUtils.isNotEmpty(list)){
            //应开票金额
            insureFeeTotal = list.stream().map(NonDirectBillVO::getInsureFeeTotal).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            //实际开票金额
            invoiceTotalAmount = list.stream().map(NonDirectBillVO::getInvoiceTotalAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        resultMap.put("insureFeeTotal", insureFeeTotal);
        resultMap.put("invoiceTotalAmount", invoiceTotalAmount);
        return resultMap;
    }

    /**
     * @Description 待审核、已开票 - 列表 - 导出
     * @Author yangxm
     * @Date 2025/7/7 15:46
     * @param nonDirectInvoiceDTO
     * @param response
     */
    @Override
    public void invoiceExport(NonDirectInvoiceDTO nonDirectInvoiceDTO, HttpServletResponse response) {
        List<NonDirectBillVO> list = selectInvoiceList(nonDirectInvoiceDTO);
        String billStatus = nonDirectInvoiceDTO.getBillStatus();
        String billStatusVal;
        if ("2".equals(billStatus)){
            billStatusVal = "待审核";
        }else if ("1".equals(billStatus)){
            billStatusVal = "已开票";
        }else {
            throw new UtilException("参数：开票状态有误！");
        }
        String fileName = "非直投发票管理-" + billStatusVal + "列表";
        log.info("导出非直投发票管理-{}列表，参数：{}", billStatusVal, nonDirectInvoiceDTO);
        List<String> arrayHeaders = new ArrayList<>();
        arrayHeaders.add("序号");
        arrayHeaders.add("开票ID");
        arrayHeaders.add("合并开票ID");
        arrayHeaders.add("税号");
        arrayHeaders.add("公司注册地址");
        arrayHeaders.add("公司注册电话");
        arrayHeaders.add("开户银行");
        arrayHeaders.add("银行账号");
        arrayHeaders.add("供应商全称");
        arrayHeaders.add("供应商简称");
        arrayHeaders.add("付款单位");
        arrayHeaders.add("开票金额");
        arrayHeaders.add("发票类型");
        arrayHeaders.add("实际开票金额");
        arrayHeaders.add("本次差额");
        arrayHeaders.add("税率");
        arrayHeaders.add("发票号码");
        arrayHeaders.add("开票日期");
        arrayHeaders.add("开票月份");
        arrayHeaders.add("发票金额");
        arrayHeaders.add("审核人");
        arrayHeaders.add("审核时间");
        arrayHeaders.add("操作时间");
        arrayHeaders.add("备注");
        //动态表头
        List<List<String>> headList=new ArrayList<>();
        List<String> head=null;
        for (String strHead:arrayHeaders) {
            head=new ArrayList<>();
            head.add(strHead);
            headList.add(head);
        }
        //动态表头的数据
        List<List<Object>> datalist=new ArrayList<>();
        List<Object> obj=null;
        NonDirectBillVO item;
        for (int i = 0; i < list.size(); i++) {
            item = list.get(i);
            obj = new ArrayList<>();
            obj.add(i + 1);
            obj.add(item.getBillId());
            obj.add(item.getBillMergeId());
            obj.add(item.getTaxNum());
            obj.add(item.getCompanyAddress());
            obj.add(item.getCompanyPhone());
            obj.add(item.getBankDeposit());
            obj.add(item.getBankNum());
            obj.add(item.getSupplyName());
            obj.add(item.getSupplyShortName());
            obj.add(item.getActualPayCompanyName());
            obj.add(item.getInsureFeeTotal() == null ? BigDecimal.ZERO : item.getInsureFeeTotal());
            obj.add(item.getInvoiceTypeVal());
            obj.add(item.getInvoiceTotalAmount() == null ? BigDecimal.ZERO : item.getInvoiceTotalAmount());
            obj.add(item.getInsureFeeTotal() == null ? BigDecimal.ZERO : item.getInsureFeeTotal().subtract(item.getInvoiceTotalAmount() == null ? BigDecimal.ZERO : item.getInvoiceTotalAmount()));
            obj.add(item.getTaxRate());
            obj.add(item.getInvoiceNums());
            obj.add(item.getInvoiceDates());
            obj.add(item.getInvoiceYearMonth());
            obj.add(item.getInvoiceMoneys());
            obj.add(item.getAuditBy());
            obj.add(Optional.ofNullable(item.getAuditTime()).map(x-> DateFormatUtils.format(x, "yyyy/MM/dd HH:mm:ss")).orElse(""));
            obj.add(Optional.ofNullable(item.getUpdateTime()).map(x-> DateFormatUtils.format(x, "yyyy/MM/dd HH:mm:ss")).orElse(""));
            obj.add(item.getRemark());
            datalist.add(obj);
        }
        try {
            EasyExcelUtil.writeExcel(response, datalist, headList, fileName, fileName);
        } catch (IOException e) {
            log.error("{}导出异常：{}", fileName, e);
            throw new RuntimeException("导出失败：" + e.getMessage());
        }
    }

    /**
     * @Description 待审核、已开票 - 合并发票 - 开票详情
     * @Author yangxm
     * @Date 2025/7/7 15:56
     * @param nonDirectInvoiceDTO
     * @return java.util.List<com.qixiaobao.nondirect.invoice.domain.Bill>
     */
    @Override
    public List<Bill> selectBillMergeDetailList(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        log.info("查询待审核、已开票 - 合并发票 - 开票详情，参数：{}", nonDirectInvoiceDTO);
        if (nonDirectInvoiceDTO.getBillMergeId() == null){
            throw new UtilException("参数错误！");
        }
        return billMergeMapper.selectBillListByMergeId(nonDirectInvoiceDTO.getBillMergeId());
    }

    /**
     * @Description 待审核、已开票 - 发票文件
     * @Author yangxm
     * @Date 2025/7/7 16:05
     * @param nonDirectInvoiceDTO
     * @return java.util.List<com.qixiaobao.nondirect.invoice.domain.InvoiceFile>
     */
    @Override
    public List<InvoiceFile> selectInvoiceFileList(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        List<InvoiceFile> list = null;
        if (nonDirectInvoiceDTO.getBillId() != null && nonDirectInvoiceDTO.getBillMergeId() == null){
            list = invoiceFileMapper.selectInvoiceFileListByBillId(nonDirectInvoiceDTO.getBillId());
        }else if (nonDirectInvoiceDTO.getBillId() == null && nonDirectInvoiceDTO.getBillMergeId() != null){
            list = invoiceFileMapper.selectInvoiceFileListByBillMergeId(nonDirectInvoiceDTO.getBillMergeId());
        }else {
            throw new UtilException("参数错误！");
        }
        return list;
    }

    /**
     * @Description 待审核 - 重新上传发票文件
     * @Author yangxm
     * @Date 2025/7/7 16:18
     * @param nonDirectInvoiceDTO
     * @return int
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int reUploadInvoiceFile(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        int row;
        if (nonDirectInvoiceDTO.getBillId() != null && nonDirectInvoiceDTO.getBillMergeId() == null){
            //清除原有上传的发票文件
            invoiceFileMapper.deleteInvoiceFileByBillId(nonDirectInvoiceDTO.getBillId());
            row = uploadInvoicingFile(nonDirectInvoiceDTO);
        }else if (nonDirectInvoiceDTO.getBillId() == null && nonDirectInvoiceDTO.getBillMergeId() != null){
            invoiceFileMapper.deleteInvoiceFileByBillMergeId(nonDirectInvoiceDTO.getBillMergeId());
            row = uploadBillMergeFile(nonDirectInvoiceDTO);
        }else {
            throw new IllegalArgumentException("发票参数异常！");
        }
        return row;
    }

    /**
     * @Description 已开票 - 根据筛选条件，下载发票文件，供应商简称必选
     * @Author yangxm
     * @Date 2025/7/7 16:40
     * @param nonDirectInvoiceDTO
     */
    @Override
    public void downloadInvoiceFile(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        LoginUser user = SecurityUtils.getLoginUser();
        if (user == null){
            throw new UtilException("登陆超时，请重新登录！");
        }
        if (StringUtils.isEmpty(nonDirectInvoiceDTO.getSupplyNameList())){
            throw new UtilException("缺少参数：供应商简称");
        }
        //开票状态：已开票
        nonDirectInvoiceDTO.setBillStatus("1");
        String operationType = "非直投发票管理-已开票";
        log.info("一键下载发票，操作人：{}, 操作页面：{}, 参数：{}", user.getUsername(), operationType, nonDirectInvoiceDTO);
        //查询需要下载的发票ID集合
        List<Long> idList = billMapper.selectInvoiceFileIdList(nonDirectInvoiceDTO);
        log.info("一键下载发票，操作人：{}, 操作页面：{}, 发票ID集合：{}", user.getUsername(), operationType, idList);
        if (StringUtils.isNotEmpty(idList)){
            //将信息放入mq
            JSONObject message = new JSONObject();
            message.put("userId", user.getId());
            message.put("client", "supply");
            message.put("ids", idList);
            message.put("supplyShortName", StringUtils.join(nonDirectInvoiceDTO.getSupplyNameList(),  ","));
            message.put("operationType", operationType);
            msgSendServer.sendDirectByRoute(RabbitMqConfig.DIRECT_EXCHANGE, RabbitMqConfig.INVOICE_ROUTING_KEY, message.toJSONString(), new CorrelationData(IdUtils.simpleUUID()));
        }
    }

    /**
     * @Description 待审核、已开票 - 发票 - 一键下载
     * @Author yangxm
     * @Date 2025/7/7 16:57
     * @param nonDirectInvoiceDTO
     * @param response
     */
    @Override
    public void invoiceFileDownload(NonDirectInvoiceDTO nonDirectInvoiceDTO, HttpServletResponse response) {
        if (nonDirectInvoiceDTO.getBillMergeId() == null && nonDirectInvoiceDTO.getBillId() == null){
            throw new UtilException("参数错误！");
        }
        String realFileName = IdUtils.fastSimpleUUID();
        // 存储发票文件的文件夹路径
        String filePath = SupplyConfig.getProfile() + FileUtils.getPath() + realFileName + "/";
        // 压缩文件的存放目录
        String zipFilePath = SupplyConfig.getProfile() + FileUtils.getPath() + System.currentTimeMillis() + ".zip";

        List<InvoiceFile> invoiceFileList = null;
        if (nonDirectInvoiceDTO.getBillId() != null){
            invoiceFileList = invoiceFileMapper.selectInvoiceFileListByBillId(nonDirectInvoiceDTO.getBillId());
        }else if (nonDirectInvoiceDTO.getBillMergeId() != null){
            invoiceFileList = invoiceFileMapper.selectInvoiceFileListByBillMergeId(nonDirectInvoiceDTO.getBillMergeId());
        }
        List<String> fileUrlList = null;
        if (StringUtils.isNotEmpty(invoiceFileList)){
            fileUrlList = invoiceFileList.stream().map(InvoiceFile::getInvoiceUrl).collect(Collectors.toList());
        }
        if (StringUtils.isEmpty(fileUrlList)){
            throw new UtilException("未查询到发票文件！");
        }
        try {
            for (int i = 0; i < fileUrlList.size(); i++) {
                log.info("发票文件url：{}", fileUrlList.get(i));
                FileUtils.downloadUsingNIO(fileUrlList.get(i), filePath);
            }
            ZipUtils.zipFile(filePath, realFileName, zipFilePath);
        }catch (Exception e){
            log.error("一键下载发票文件异常，异常信息：",  e);
            throw new UtilException("发票文件一键下载失败，异常信息：" + e.getMessage());
        }finally {
            FileUtils.deleteDirectory(new File(filePath));
            FileUtils.deleteDirectory(new File(zipFilePath));
        }

    }

    /**
     * @Description 合并发票文件上传
     * @Author yangxm
     * @Date 2025/7/7 16:27
     * @param nonDirectInvoiceDTO
     */
    private int uploadBillMergeFile(NonDirectInvoiceDTO nonDirectInvoiceDTO) {
        if (nonDirectInvoiceDTO.getBillMergeId() == null){
            throw new UtilException("参数错误！");
        }
        if (StringUtils.isEmpty(nonDirectInvoiceDTO.getOcrFileList())){
            throw new UtilException("请上传发票！");
        }
        BillMerge oldBillMerge = billMergeMapper.selectBillMergeById(nonDirectInvoiceDTO.getBillMergeId());
        if (oldBillMerge == null){
            throw new UtilException("数据不存在，请刷新页面后重试！");
        }
        BillMerge billMerge = new BillMerge();
        billMerge.setId(nonDirectInvoiceDTO.getBillMergeId());
        //开票状态：待审核
        billMerge.setBillStatus("2");
        //财务审核状态：待审
        billMerge.setAuditStatus("0");
        //发票金额
        billMerge.setInvoiceTotalAmount(nonDirectInvoiceDTO.getInvoiceTotalAmount());
        //发票税率
        billMerge.setTaxRate(nonDirectInvoiceDTO.getTaxRate());
        //发票类型
        billMerge.setInvoiceTypeVal(nonDirectInvoiceDTO.getInvoiceTypeVal());
        int row = billMergeMapper.updateBillMerge(billMerge);
        //保存发票文件
        saveInvoiceFile(nonDirectInvoiceDTO, null, billMerge);
        return row;
    }
}
